﻿#! /usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import threading

try:
    import Tkinter as tk
    from tkFileDialog import askopenfilename
    from tkFileDialog import askdirectory
except:
    #python3.x
    import tkinter as tk
    from tkinter.ttk import *
    from tkinter.filedialog import askopenfilename
    from tkinter.filedialog import askdirectory

###############################TK_UI_封装#############################
######################################################################
g_default_theme = "dark"
init_path = os.path.dirname(os.path.realpath(__file__))

class PyButton(tk.Button):
    '''
    Button
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Button.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "activebackground": "#00B2EE",
                            "activeforeground": "#E0EEEE",
                            "bg": "#008B8B",
                            "fg": "#FFFFFF"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyLabel(tk.Label):
    '''
    Label
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Label.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#E0EEEE"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyLabelFrame(tk.LabelFrame):
    '''
    Label
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.LabelFrame.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#1E90FF"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyListbox(tk.Listbox):
    '''
    Listbox
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Listbox.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#1E90FF",
                            "selectbackground": "#00B2EE"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyText(tk.Text):
    '''
    Text
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Text.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#1E90FF"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyCheckbutton(tk.Checkbutton):
    '''
    Checkbutton
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Checkbutton.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#FFFFFF",
                            "activebackground": "#292929",
                            "selectcolor": "#292929"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

class PyEntry(tk.Entry):
    '''
    Entry
    '''
    def __init__(self, master, theme=g_default_theme, **kv):
        self.theme = theme
        self.kv = kv
        self.choose_theme()
        tk.Entry.__init__(self, master, self.kv) 

    def choose_theme(self):
        dark_theme_dict = {
                            "bg": "#292929",
                            "fg": "#E0EEEE"
                          }
        for key,value in dark_theme_dict.items():
            self.kv[key] = value

        for key,value in self.kv.items():
            self.kv[key] = value

###############################界面UI#############################
##################################################################

size_dict = {
                "path_entry_width": 40,
                "path_btn_width": 8,
                "rename_entry_width": 14
            }

class PyinstallerUI(object):
    def __init__(self, master=None):
        self.root = master
        self.create_frame()

    def create_frame(self):
        '''
        新建窗口，分为上下2个部分，下半部分为状态栏
        '''
        self.frm = PyLabelFrame(self.root, 
                                     text="", 
                                     bg="#292929", 
                                     fg="#1E90FF")
        self.frm_status = PyLabelFrame(self.root, 
                                            text="", 
                                            bg="#292929", 
                                            fg="#1E90FF")

        self.frm.grid(row=0, column=0, sticky="wesn")
        self.frm_status.grid(row=1, column=0, sticky="wesn")

        self.create_frm()
        self.create_frm_status()

    def create_frm(self):
        '''
        上半部分窗口分为上中2个部分加一个按钮
        '''
        self.frm_top = PyLabelFrame(self.frm)
        self.frm_middle = PyLabelFrame(self.frm)
        self.frm_btn_generate = PyButton(self.frm, 
                                              text="开始打包exe",
                                              font=("Monaco", 12),
                                              command=self.Generate)

        self.frm_top.grid(row=0, column=0, padx=5, pady=5, sticky="wesn")
        self.frm_middle.grid(row=1, column=0, padx=5, pady=5, sticky="wesn")
        self.frm_btn_generate.grid(row=2, column=0, padx=5, pady=5, sticky="wesn")

        self.create_frm_top()
        self.create_frm_middle()

    def create_frm_top(self):
        '''
        上半部分顶部窗口：
        1、选择源文件
        2、选择工作路径（中间文件）
        3、选择生成exe的路径
        4、选择ico的路径
        '''
        label_text_list = ["py 文件:", 
                           "工作路径:", 
                           "目录路径:", 
                           "ICO图标:"]

        self.entry_value_list = list()
        for index,label_text in enumerate(label_text_list):
            temp_strvar = tk.StringVar()
            temp_label = PyLabel(self.frm_top, 
                                      font=("Monaco", 12), 
                                      text=label_text)
            temp_entry = PyEntry(self.frm_top, 
                                      width=size_dict["path_entry_width"], 
                                      font=("Monaco", 12), 
                                      textvariable=temp_strvar)
            self.entry_value_list.append(temp_strvar)
            temp_label.grid(row=index%4, column=0, padx=5, pady=5, sticky="w")
            temp_entry.grid(row=index%4, column=1, padx=5, pady=5, sticky="w")

        self.frm_top_btn_source_file = PyButton(self.frm_top, 
                                       text="...", 
                                       width=size_dict["path_btn_width"], 
                                       font=("Monaco", 8),
                                       command=self.ChooseSourceFile)
        self.frm_top_btn_work_path = PyButton(self.frm_top, 
                                       text="...", 
                                       width=size_dict["path_btn_width"], 
                                       font=("Monaco", 8),
                                       command=self.ChooseWorkPath)
        self.frm_top_btn_dist_path = PyButton(self.frm_top, 
                                       text="...", 
                                       width=size_dict["path_btn_width"], 
                                       font=("Monaco", 8),
                                       command=self.ChooseDistPath)
        self.frm_top_btn_ico_file = PyButton(self.frm_top, 
                                       text="...", 
                                       width=size_dict["path_btn_width"], 
                                       font=("Monaco", 8),
                                       command=self.ChooseICOFile)
        self.frm_top_btn_source_file.grid(row=0, column=2, padx=5, pady=5, sticky="w")
        self.frm_top_btn_work_path.grid(row=1, column=2, padx=5, pady=5, sticky="w")
        self.frm_top_btn_dist_path.grid(row=2, column=2, padx=5, pady=5, sticky="w")
        self.frm_top_btn_ico_file.grid(row=3, column=2, padx=5, pady=5, sticky="w")

    def create_frm_middle(self):
        '''
        常用的三个附加属性选择
        '''
        self.onefile = tk.IntVar()
        self.noconsole = tk.IntVar()
        #self.python2 = tk.IntVar()
        self.python3 = tk.IntVar()
        self.frm_middle_cbtn_isonefile = PyCheckbutton(self.frm_middle,
                                         text="单文件",
                                         variable=self.onefile,
                                         font=("Monaco", 15),
                                         relief="flat")
        self.frm_middle_cbtn_noconsole = PyCheckbutton(self.frm_middle,
                                         text="无命令窗",
                                         variable=self.noconsole,
                                         font=("Monaco", 15),
                                         relief="flat")
        self.frm_middle_cbtn_python3 = PyCheckbutton(self.frm_middle,
                                          text="PYTHON3(PYTHON2)",
                                          variable=self.python3,
                                          font=("Monaco", 15),
                                          relief="flat")
        self.frm_middle_cbtn_isonefile.grid(row=0, column=0, padx=5, pady=5, sticky="w")
        self.frm_middle_cbtn_noconsole.grid(row=0, column=1, padx=5, pady=5, sticky="w")
        #self.frm_middle_cbtn_python2.grid(row=0, column=2, padx=5, pady=5, sticky="w")
        self.frm_middle_cbtn_python3.grid(row=0, column=2, padx=5, pady=5, sticky="w")     

        self.onefile.set(1)
        self.noconsole.set(1)
        self.python3.set(1)

    def create_frm_status(self):
        '''
        下半部分状态栏窗口
        '''
        self.frm_status_label = PyLabel(self.frm_status, 
                                             text="Ready",
                                             font=("Monaco", 12))
        self.frm_status_label.grid(row=1, column=0, padx=5, pady=5, sticky="wesn")

    def Generate(self):
        '''
        生成可执行文件
        '''
        pass

    def ChooseSourceFile(self):
        '''
        选择源文件
        '''
        source_file = askopenfilename(initialdir = init_path,filetypes=(("py files", "*.py"),
                                                ("All files", "*.*")))
        if not source_file:
            return
        default_path = os.path.dirname(source_file)
        self.entry_value_list[0].set(source_file)
        self.entry_value_list[1].set(default_path)
        self.entry_value_list[2].set(default_path)

    def ChooseWorkPath(self):
        '''
        选择工作路径（中间文件存放的文件夹）
        '''
        work_path = askdirectory(initialdir = init_path)
        if not work_path:
            return
        self.entry_value_list[1].set(work_path)

    def ChooseDistPath(self):
        '''
        选择生成文件路径（exe）
        '''
        dist_path = askdirectory(initialdir = init_path)
        if not dist_path:
            return
        self.entry_value_list[2].set(dist_path)

    def ChooseICOFile(self):
        '''
        选择ico文件
        '''
        ico_file = askopenfilename(initialdir = init_path,filetypes=(("ico files", "*.ico"),
                                             ("All files", "*.*")))
        if not ico_file:
            return
        self.entry_value_list[3].set(ico_file)


###############################主功能模块#############################
######################################################################


class MainPyinstaller(PyinstallerUI):
    def __init__(self, master=None):
        super(MainPyinstaller, self).__init__()
        self.start_flag = False

    def Generate(self):
        '''
        生成可执行文件
        '''
        if not self.start_flag:
            thread_generate = threading.Thread(target=self.ThreadGenerate)
            thread_generate.setDaemon(True)
            thread_generate.start()
        else:
            self.frm_status_label["text"] = "Being Packing, please re-run later!"

    def ThreadGenerate(self):
        '''
        线程执行生成动作
        '''
        if len(self.entry_value_list[0].get()) == 0:
            self.frm_status_label["text"] = "Please select the source file!"
            return
        try:
            self.start_flag = True
            command = self.assembly_command_string()
            print(command)
            self.frm_status_label["text"] = "Being Packing, please wait a moment!"
            os.system(command)
            self.start_flag = False
            self.frm_status_label["text"] = "Complete package!"
        except Exception as e:
            self.frm_status_label["text"] = e.message
            self.start_flag = False

    def assembly_command_string(self):
        '''
        组装终端执行命令行
        '''
        self.command_string = ""
        self.command_list = list()

        if self.python3.get() == 1:
            self.command_list.append("pyinstaller")
        else:
            self.command_list.append("C:\\Python27\\Scripts\\pyinstaller.exe")

        if len(self.entry_value_list[1].get()) > 0:
            self.command_list.append("--workpath=" + self.entry_value_list[1].get())

        if len(self.entry_value_list[2].get()) > 0:
            self.command_list.append("--distpath=" + self.entry_value_list[2].get())

        if len(self.entry_value_list[3].get()) > 0:
            self.command_list.append("--icon=" + self.entry_value_list[3].get())

        if self.onefile.get() == 1:
            self.command_list.append("--onefile")

        if self.noconsole.get() == 1:
            self.command_list.append("--noconsole")
            
        if len(self.entry_value_list[0].get()) > 0:
            self.command_list.append(self.entry_value_list[0].get())

        self.command_string = " ".join(self.command_list)
        return self.command_string



if __name__ == '__main__':
    '''
    main loop
    '''
    root = tk.Tk()
    root.title("Pyinstaller 打包工具")
    MainPyinstaller(master=root)
    root.resizable(False, False)
    root.mainloop()